package Control;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class MonThread {
	
	
	private String CallPutFlag;
	private double S;
	private double X;
	private double T;
	private double r;
	private double b;
	private double v;
	private int nSteps;
	private int nSimulations;
 
	public MonThread(   String CallPutFlag, double S, double X, double T, double r, double b, double v, int nSteps, int nSimulations    ) {
	
		this.CallPutFlag=CallPutFlag;
		this.S=S;
		this.X=X;
		this.T=T;
		this.r=r;
		this.b=b;
		this.v=v;
		this.nSteps=nSteps;
		this.nSimulations=nSimulations;
	}
	
	  public double compute() throws InterruptedException, ExecutionException{
		  double dt, Sum = 0.0;
		  final double Drift;
		  final double vSqrdt;
		  int i, z=0;
		  
		  final int numOfCores = Runtime.getRuntime().availableProcessors();
		     final double CoreCoeff = 0.1;
		     final int poolSize = (int)(numOfCores / (1 - CoreCoeff));
		     final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
		  
		  dt = this.T / this.nSteps;
		  Drift = (this.b - Math.pow(this.v,2) / 2) * dt;
		  vSqrdt = this.v * Math.sqrt(dt);
		  
		  if(this.CallPutFlag.compareTo("c")==0)
		  {
		   z=1;
		  }
		  else
		  {
		   z=-1;
		  }
		  
		  final double zf = z;
		  
		  for(i=1;i<=this.nSimulations;i++){
		   partitions.add(new Callable<Double>() {
		    @Override
		    public Double call() throws InterruptedException, ExecutionException{
		     
		     double St = S;
		     for(int j=1;j<=nSteps;j++){
		      St = St * Math.exp(Drift + vSqrdt * (new Random().nextGaussian()));
		     }
		     return Math.max(zf*(St - X), 0);
		    }
		   });
		  }
		  
		  final ExecutorService executor = Executors.newFixedThreadPool(poolSize);
		  final List<Future<Double>> valueOfReturn = executor.invokeAll(partitions, 10000, TimeUnit.SECONDS);
		  
		  for(final Future<Double> value: valueOfReturn)
		   Sum+= value.get();
		  
		  executor.shutdown();
		  
		  return Math.exp(-this.r * this.T) * (Sum / this.nSimulations);
	  }
	
}

